package com.yunkss.eftool.crypto;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.Arrays;

import java.io.IOException;
import java.math.BigInteger;

/**
 * @Author csx
 * @Date 2024/4/26 20:17
 * @Desc SM2Util
 **/
public class SM2Util {

    private static final int C1_LEN = 65;
    private static final int C3_LEN = 32;

    /**
     * 将c1c3c2转成标准的ASN1格式
     */
    public static byte[] changeC1C3C2ToAsn1(byte[] c1c3c2) throws IOException {
        byte[] c1 = Arrays.copyOfRange(c1c3c2, 0, C1_LEN);
        byte[] c3 = Arrays.copyOfRange(c1c3c2, C1_LEN, C1_LEN + C3_LEN);
        byte[] c2 = Arrays.copyOfRange(c1c3c2, C1_LEN + C3_LEN, c1c3c2.length);
        byte[] c1X = Arrays.copyOfRange(c1, 1, 33);
        byte[] c1Y = Arrays.copyOfRange(c1, 33, 65);

        BigInteger r = new BigInteger(1, c1X);
        BigInteger s = new BigInteger(1, c1Y);

        ASN1Integer x = new ASN1Integer(r);
        ASN1Integer y = new ASN1Integer(s);
        DEROctetString derDig = new DEROctetString(c3);
        DEROctetString derEnc = new DEROctetString(c2);
        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(x);
        v.add(y);
        v.add(derDig);
        v.add(derEnc);
        DERSequence seq = new DERSequence(v);
        return seq.getEncoded(ASN1Encoding.DER);
    }

    /**
     * 将ASN1格式转成c1c3c2
     */
    public static byte[] changeAsn1ToC1C3C2(byte[] asn1) throws IOException {
        ASN1InputStream aIn = new ASN1InputStream(asn1);
        ASN1Sequence seq = (ASN1Sequence) aIn.readObject();
        BigInteger x = ASN1Integer.getInstance(seq.getObjectAt(0)).getValue();
        BigInteger y = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue();
        byte[] c3 = ASN1OctetString.getInstance(seq.getObjectAt(2)).getOctets();
        byte[] c2 = ASN1OctetString.getInstance(seq.getObjectAt(3)).getOctets();

        ECPoint c1Point = GMNamedCurves.getByName("sm2p256v1").getCurve().createPoint(x, y);
        byte[] c1 = c1Point.getEncoded(false);
        return ArrayUtil.addAll(c1, c3, c2);
    }

    public static void main(String[] args) throws IOException {
        //需要加密的明文
        String text = "320fa6ec18d0fc80ca56205eea47617b3988447f69cb3597c4fb3da361fe03e3";
        //创建sm2 对象
        SM2 sm2 = SmUtil.sm2();
        //生成公钥
//        byte[] publicKey = ((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false);
        //生成私钥
//        byte[] privateKey = BCUtil.encodeECPrivateKey(sm2.getPrivateKey());
        //打印当前的公私秘钥
//        System.out.println("公钥: " + HexUtil.encodeHexStr(publicKey));
//        System.out.println("私钥: " + HexUtil.encodeHexStr(privateKey));

        byte[] publicKey = HexUtil.decodeHex("04ba3bc3c5834d9ad1a7d81c4b49cf1209d2f28e4a97c73b75d6561792a2bfafe811e0284692006b0ce1b51f8aadfd65241d80eb979365048253408f5d705ec17b");

        byte[] privateKey = HexUtil.decodeHex("7713d336bcbbffb8b7f9cab8db984a5c989a0b07697f569a06d5cd38e1351d07");

        SM2 sm2Test = SmUtil.sm2(privateKey, publicKey);
        byte[] encrypt = sm2Test.encrypt(text, KeyType.PublicKey);

        byte[] bytes = changeC1C3C2ToAsn1(encrypt);

        String str = HexUtil.encodeHexStr(bytes);

        //3081a90220114fa0096a3ee81ac68ca744c1742fe5351baab7abdce13ba9ca76a16a8ed553022100d6d2691d997e98422b6b21020b5048636d0e033fb4e07789e17083bbce71ec8e042094f9bd3a92acf5de53f8f3c49e96347540cd34b078ebaea67d3df5a7c6093091044016914f09d0f22e4ae1fbcf7c6f20d378166043e43e4160b3b91a3c4b442611f7aaa0ebd986173a843535b5d67cbf0e0d603f61a22f08f10c1d92578cc1b3d8f8
        System.out.println("加密后：" + str);

        byte[] asn1 = HexUtil.decodeHex(str);

        byte[] c1c3c2 = changeAsn1ToC1C3C2(asn1);
        byte[] decryptData = sm2Test.decrypt(c1c3c2, KeyType.PrivateKey);
        System.out.println("解密后：" + new String(decryptData));

        byte[] sign = sm2Test.sign(text.getBytes());
        String signText = Base64.encode(sign);
        System.out.println("签名：" + signText);
        boolean verify = sm2Test.verify(text.getBytes(), Base64.decode(signText));
        System.out.println("验签结果：：" + verify);
    }

}
